/**
 * Heterotopia - an up-to-date Java-based MUD system
 * Copyright (C) 2011 Bill Garrett <memesis@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package heterotopia.script;

import heterotopia.oa.DurableObject;
import heterotopia.oa.DurableObjectDao;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.apache.camel.EndpointInject;
import org.apache.camel.Exchange;
import org.apache.camel.InOut;
import org.apache.camel.Processor;
import org.apache.camel.ProducerTemplate;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.transaction.annotation.Transactional;

/**
 * A class for processing Exchanges by executing scripts in one or more
 * supported languages.
 * 
 * @author garrett
 */
public class ScriptEngineProcessor implements InitializingBean, Processor {
	private ScriptEngineManager mgr;
	private Map<String, ScriptEngine> engines;
	private Resource[] scriptLocations;
	private DurableObjectDao dao;
	
	@EndpointInject(uri="direct:scriptEngineProcessor-sessions")
	protected ProducerTemplate producer;

	public ScriptEngineProcessor() {
		mgr = new ScriptEngineManager();
		engines = new HashMap<String, ScriptEngine>();
	}
	
	public void setLocation(Resource[] scriptLocations) {
		this.scriptLocations = scriptLocations;
	}
	
	public void setDao(DurableObjectDao dao) {
		this.dao = dao;
	}
	
	public void afterPropertiesSet() throws Exception {
		for(Resource r: scriptLocations) {
			addResourceToEngine(r);
		}
	}

	/* (non-Javadoc)
	 * @see org.apache.camel.Processor#process(org.apache.camel.Exchange)
	 */
	@InOut
	@Transactional
	public void process(Exchange exchange) throws Exception {		
		DurableObject o = dao.find("402880913042f358013042f35ff00001");
		ScriptEngine engine = findOrCreateEngineByExtension(o.getLanguage());
		Invocable invoke = (Invocable) engine;
		
		try {
			Object callableScriptObject = invoke.invokeFunction("activate_object", o);
			invoke.invokeMethod(callableScriptObject, "process", exchange);
			invoke.invokeFunction("deactivate_object", o, callableScriptObject);
		} finally {
			dao.persist(o);
		}
	}
	
	private String extractStackTrace(Throwable ex) {
		Throwable t = ex;
		StringWriter s = new StringWriter();
		PrintWriter p = new PrintWriter(s);
		while(t != null) {
			t.printStackTrace(p);
			t = t.getCause();
		}
		return s.toString();
	}
	
	private void addResourceToEngine(Resource r) throws ScriptException, IOException {
		String extension = getExtension(r.getFilename());
		ScriptEngine engine = findOrCreateEngineByExtension(extension);
		
		if(engine != null) {
			engine.eval(new InputStreamReader(r.getInputStream()));
		}
	}
	
	private ScriptEngine findOrCreateEngineByExtension(String fileExtension) {
		// Try to find an already-existing engine
		if(engines.containsKey(fileExtension))
			return engines.get(fileExtension);

		ScriptEngine engine = mgr.getEngineByExtension(fileExtension);
		engine.put("producer", producer);
		engine.put("dao", dao);
		engines.put(fileExtension, engine);
		return engine;
	}
	
	private static String getExtension(String filepath) {
        String ext = null;
        int i = filepath.lastIndexOf('.');

        if (i > 0 &&  i < filepath.length() - 1) {
            ext = filepath.substring(i+1).toLowerCase();
        }
        return ext;
    }
}